/*-
 * ListPDU.java
 *
 * Copyright (c) 2007, Stein Gunnar Bakkeby
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Stein Gunnar Bakkeby nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.googlecode.attention.at;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.googlecode.attention.PDUEntry;
import com.googlecode.attention.SMSEntry;
import com.googlecode.attention.Status;


/**
 * Lists SMS messages from preferred store.
 *
 * @author Stein Gunnar Bakkeby
 */
public class ListPDU extends AtCommand<ListPDU> {
  
  private Status[] supportedStatuses = new Status[] { Status.UNKNOWN };
  private ArrayList<SMSEntry> pdus = new ArrayList<SMSEntry>();
  private boolean updateStatus = true;
  
  public ListPDU(final AtListener<ListPDU> listener) {
    super(listener);
  }
  
  /**
   * If set to false, then the status of unread messages
   * in the storage will not be changed to read. 
   * <BR>
   * <BR>This value defaults to true.
   * 
   * @param value True if status should be changed, false otherwise 
   */
  public void setUpdateStatus(final boolean value) {
    updateStatus = value;
  }
  
  /**
   * This will return true if the status of unread messages in the
   * storage will be changed to read, otherwise it will return false.
   * 
   * @return A boolean value
   */
  public boolean getUpdateStatus() {
    return updateStatus;
  }
  
  /** Lists supported statuses. */
  public void prepareTest() {
    if (updateStatus) {
      setCommand("AT+CMGL=?", Command.TEST);
    } else {
      setCommand("AT^SMGL=?", Command.TEST);
    }
  }
  
  /**
   * Lists SMS messages from preferred store with default status.
   * 
   * @see #prepareExecute(Status)
   */
  public void prepareExecute() {
    if (updateStatus) {
      setCommand("AT+CMGL", Command.EXECUTE);
    } else {
      setCommand("AT^SMGL", Command.EXECUTE);
    }
  }
  
  /**
   * Lists SMS messages from preferred store.
   * 
   * @param status The status of the messages to be listed
   */
  public void prepareExecute(Status status) {
    if (updateStatus) {
      setCommand("AT+CMGL=" + status.getValue(), Command.EXECUTE);
    } else {
      setCommand("AT^SGML=" + status.getValue(), Command.EXECUTE);
    }
  }
  
  @Override
  protected void parseTest(final String response) {
    /*
     * Regex for AT+CMGL=?:
     * (?:\+CMGL:|\^SMGL:) \((\d)-(\d)\)
     * 
     * Group(0) = +CMGL: (0-4)
     * Group(1) = 0         <--- Min status
     * Group(2) = 4         <--- Max status
     */
    final Pattern pattern = Pattern.compile(
        "(?:\\+CMGL:|\\^SMGL:) \\((\\d)-(\\d)\\)");
    final Matcher matcher = pattern.matcher(response);
    if (matcher.find()) {
      final int minStatus = Integer.parseInt(matcher.group(1));
      final int maxStatus = Integer.parseInt(matcher.group(2));
      supportedStatuses = new Status[maxStatus - minStatus + 1];
      for (int i = minStatus; i <= maxStatus; i++) {
        supportedStatuses[i] = Status.getStatus(i);
      }
    }
  }
  
  @Override
  protected void parseExecute(final String response) {
    /*
     * Regex for AT+CMGL=[0-4] in PDU mode.
     * (?:\+CMGL:|\^SMGL:) (\d+),(\d),([+A-Za-z0-9]+)?,(\d+)\r?\n([0-9A-Fa-f]+)
     * 
     * Group(0) = +CMGL: 1,1,,152\r\n0791449785788908040C9144970192005900007070209053354098C8329A053AA7D76B5078BDAECBC37490F92D17A7416537489C6683DE67D0FC019A3F401D50990E9A3FDC6ED0515E0ED34142799A1E4EBB41EBB09C0E62A7CFE73208FE0089C3EB79995EA6B340EF3368FE00D1CBEE35BD0CEA80C274D0FCE17683D06139C89E06BDCFF3070BD42EBB41F307C81E069141F6877C0E12A7D92E970B544693CF
     * Group(1) = 1         <--- Memory Index
     * Group(2) = 1         <--- Status (1 == received read)
     * Group(3) = null      <--- Alpha (string repr. of number, can be null)
     * Group(4) = 152       <--- Length of PDU
     * Group(5) = 0791449785788908040C9144970192005900007070209053354098C8329A053AA7D76B5078BDAECBC37490F92D17A7416537489C6683DE67D0FC019A3F401D50990E9A3FDC6ED0515E0ED34142799A1E4EBB41EBB09C0E62A7CFE73208FE0089C3EB79995EA6B340EF3368FE00D1CBEE35BD0CEA80C274D0FCE17683D06139C89E06BDCFF3070BD42EBB41F307C81E069141F6877C0E12A7D92E970B544693CF
     * 
     */
    final Pattern pattern = Pattern.compile(
        "(?:\\+CMGL:|\\^SMGL:) (\\d+),(\\d),([+A-Za-z0-9]+)?,(\\d+)\\r?\\n([0-9A-Fa-f]+)");
    final Matcher matcher = pattern.matcher(response);
    while (matcher.find()) {
      final PDUEntry pdu = new PDUEntry();
      pdu.setMemoryIndex(Integer.parseInt(matcher.group(1)));
      pdu.setStatus(Status.getStatus(matcher.group(2).charAt(0)));
      pdu.setAlpha(matcher.group(3));
      pdu.setLength(Integer.parseInt(matcher.group(4)));
      pdu.setPDU(matcher.group(5));
      pdus.add(pdu);
    }
  }
  
  /**
   * This will return a list of supported statuses.
   * 
   * @return A list of Status enums
   */
  public Status[] getSupportedStatuses() {
    return supportedStatuses;
  }
  
  /**
   * This will return a list of PDUs.
   * 
   * @return An ArrayList
   */
  public SMSEntry[] getPDUs() {
    return pdus.toArray(new SMSEntry[pdus.size()]);
  }

  @Override
  protected ListPDU getThis() {
    return this;
  }
}
